package com.dp1.deliversoft.algorithm;

import java.util.ArrayList;

import com.dp1.deliversoft.entidad.Constantes;
import com.dp1.deliversoft.entidad.Mapa;
import com.dp1.deliversoft.entidad.Pedido;
import com.dp1.deliversoft.entidad.Ruta;
import com.dp1.deliversoft.entidad.resultados.MaximoMinimo;

public class Grasp2 {
	
	public static Ruta GraspPrincipal(ArrayList<Pedido> pedidos, Mapa mapa) {
		
		ArrayList<Pedido> S=new ArrayList<Pedido>();
		ArrayList<Pedido> CasasSiguientesAceptables;
		Pedido opcionCasaSiguiente;
		Ruta ruta = new Ruta();
		MaximoMinimo res;
		double tiempoAcum=0;
		double alpha=0.2;
		int d_extra,retraso=0;
		int cantCasasPorRecorrer = pedidos.size(); 
	
		int x_st = mapa.getUbicacionAlmacen().getX(); //seran las coordenadas de la ultima entrega o del almacen por defecto
		int y_st= mapa.getUbicacionAlmacen().getY(); 
		
	
			while (cantCasasPorRecorrer > 0) {			
			/*Primero halla los limites para los costos en funcion a la casa mas lejana y mas cercana, dentro del rango definido por estos limites se considerara como 'aceptable' la visita a una casa siguiente.. estas opciones de casas aceptables se guardan en la lista de resultados*/
			res=MaxMinFuncionCostoKm(pedidos,x_st,y_st,tiempoAcum);
			
			/*hallamos la region de soluciones factibles*/
			CasasSiguientesAceptables=new ArrayList<Pedido>();
			CasasSiguientesAceptables = rangoAceptacion(pedidos, cantCasasPorRecorrer,res,alpha,x_st,y_st,tiempoAcum); 
			
			/*elegimos aleatoriaente una casa de la region de soluciones factibles*/
			opcionCasaSiguiente = elegirRandom(CasasSiguientesAceptables);
			/*
			 * if (cumplePlazo(opcionCasaSiguiente) //&&
			 * algunaOtraRestriccion(opcionCasaSiguiente) //Not sure if debe ir
			 * la corroboracion del plazo aqui.. {
			 */
			cantCasasPorRecorrer--;
			opcionCasaSiguiente.setVis(true); //se marca como visitado el pedido que se entrego
			/* ACA DEBE IR UNA MODIFICACION, YA QUE ESTA FLAG DE VISITADO SOLAMENTE DEBERIA SER PARA LA SOLUCION FINAL... YA QUE ESTA NO ES UNA SOLUCION FINAL DEBERIAMOS TENER UN OBJETO PEDIDO NUEVO QUE NO SEA EL DE LA LISTA ORIGINAL DE PEDIDOS Y AHI MARCAR EL FLAG */
			S.add(opcionCasaSiguiente);
			
			//System.out.println((double)Calculos.CalcDistanciaEnKm(x_st, y_st, opcionCasaSiguiente.getPosX(),  opcionCasaSiguiente.getPosY())/Constantes.velocidadAuto);
			//aumentamos tiempo acumulado en base a distancia/velocidad
			tiempoAcum=tiempoAcum+(double)Calculos.CalcDistanciaEnKm(x_st, y_st, opcionCasaSiguiente.getPosX(),  opcionCasaSiguiente.getPosY())/Constantes.velocidadAuto;
			
			//actualizamos retraso acumulado
			retraso+=Math.max(-1*(opcionCasaSiguiente.getPlazo()-tiempoAcum),0);
			
			//se actualizo coordenadas x y actuales segun casa elegida y marcada como visitada
			x_st=opcionCasaSiguiente.getPosX();
			y_st=opcionCasaSiguiente.getPosY();
			
		}
			//al costo de la ruta le sumaremos el costo inicial de ir del almacen a la primera casa
			x_st = mapa.getUbicacionAlmacen().getX(); //seran las coordenadas de la ultima entrega o del almacen por defecto
			y_st= mapa.getUbicacionAlmacen().getY(); 
			
			d_extra=Calculos.CalcDistanciaEnKm(x_st, y_st, S.get(0).getPosX(),  S.get(0).getPosY());
			
			ruta.setCostoRuta(Calculos.calcularCostoRuta(S)+d_extra+retraso*Constantes.costoRetraso);
			//se tiene costo solo en base a distancia
			
			System.out.println("cantidad de retrasos: "+retraso);
			System.out.println("tiempo acumulado "+tiempoAcum);
			ruta.setPedidos(S);
			//falta darle ID a la variable ruta
			return ruta;
	}
	
	public static MaximoMinimo MaxMinFuncionCostoKm(ArrayList<Pedido> pedidos, int x, int y,double tiempoTranscurrido)
	{
		/* se analiza distancia en base a las coordenadas x,y*/
		MaximoMinimo res=new MaximoMinimo();
		int d_x, d_y,distancia;
		double menorCostoKm, mayorCostoKm;
		int costoKms;
		int iMin=0, iMax=0;
		boolean h1=false, h2=false;
	
		menorCostoKm=1000000000; //mejor
		mayorCostoKm=-1; //peor

		for (int i=0; i<pedidos.size();i++)
		{
			d_x = pedidos.get(i).getPosX(); //posicion del destino
			d_y = pedidos.get(i).getPosY();
			if (!pedidos.get(i).isVis())
			{
				//costoKms tiene un costo aprox en base a distancia y plazo (decision)
				distancia= Calculos.CalcDistanciaEnKm(x,y, d_x, d_y);
				costoKms =(int)Calculos.FuncionDecisionGraspAnterior(distancia, pedidos.get(i).getPlazo()-tiempoTranscurrido);
				if (costoKms<=menorCostoKm)
				{ 
					menorCostoKm=costoKms; 
					h1 = true; 
					iMin=i;
				}
				if (costoKms>=mayorCostoKm)
				{ 
					mayorCostoKm=costoKms; 
					h2 = true; 
					iMax=i;
				}
			}
		}
		
		if (h1) /*se encontro distancia minima*/
		{
			res.setMejorValor(menorCostoKm); 
			res.setPmin(pedidos.get(iMin)); 
		}
		else
		{
			res.setMejorValor(-1);
			res.setPmin(null);
		}
		
		if (h2)  /*se encontro distancia maxima*/
		{
			res.setPeorValor(mayorCostoKm); 
			res.setPmax(pedidos.get(iMax));
		}
		else
		{
			res.setPeorValor(-1);
			res.setPmax(null);
		}
		
		return res;
	}
	
	public static ArrayList<Pedido> rangoAceptacion(ArrayList<Pedido> pedidos, int cantCasasPorRecorrer, MaximoMinimo res, double alpha, int x, int y,double tiempoTranscurrido)
	{
		/*implemented by carlos xdxdxd*/ /*jajaja con disclaimer todavia xD*/ 
		ArrayList<Pedido> casasAceptables = new ArrayList<Pedido>(); 
		int dist_respecto_punto_donde_esta=0;
		double costoE; //estimado
		for (int i=0;i<pedidos.size();i++)
		{
			if (!pedidos.get(i).isVis())
			{
				dist_respecto_punto_donde_esta=Calculos.CalcDistanciaEnKm(x,y,pedidos.get(i).getPosX(),pedidos.get(i).getPosY());
				
				costoE=Calculos.FuncionDecisionGraspAnterior(dist_respecto_punto_donde_esta, pedidos.get(i).getPlazo()-tiempoTranscurrido);
				
				if (((int)costoE>=(int)res.getMejorValor()) && ((int)costoE<=((int)res.getMejorValor()+alpha*((int)res.getPeorValor()-(int)res.getMejorValor()))))
				{
					casasAceptables.add(pedidos.get(i));
				}
			}
		}
		return casasAceptables;
	}
	
	public static Pedido elegirRandom(ArrayList<Pedido> casasSiguientesAceptables) 
	{
		//System.out.println("size "+casasSiguientesAceptables.size());
		double n=Math.random();
		//System.out.println("aleat "+n);
		int pos_aleat=(int)Math.round(n*(casasSiguientesAceptables.size()-1));
		//System.out.println("posicion "+pos_aleat);
		return casasSiguientesAceptables.get(pos_aleat);
	}
	
	
}
